home *** CD-ROM | disk | FTP | other *** search
/ HyperLib 1997 Winter - Disc 1 / HYPERLIB-1997-Winter-CD1.ISO.7z / HYPERLIB-1997-Winter-CD1.ISO / オンラインウェア / UTIL / Alpha 6.5.sit / Tcl / Modes / fortranMode.tcl < prev    next >
Text File  |  1996-08-15  |  21KB  |  670 lines

  1. #=============================================================================
  2. # Fortran mode definition and support procs
  3. #
  4. # Features:
  5. # 1.  Keyword colorization (slightly customizable)
  6. # 2.  Fortran-sensitive shift right/left preserve columns 1-6
  7. # 3.  Auto-indentation
  8. # 4.  Line-breaking with Ctl-Opt-J (a la emacs)
  9. # 5.  Subroutine indexing
  10. # 6.  Cmd-double-click subroutine and include-file lookup
  11. # 7.  Customizable comment and continuation characters
  12. #
  13. #------------------------------------------------------------------------------
  14. # Author: Tom Pollard <pollard@chem.columbia.edu>
  15. #
  16. # To Do:  work around grep failure for Unix-format tag files
  17. #
  18. #  1/96 - FortMarkFile no longer marks F90 "end subroutine ..." statements
  19. #         more F90 keywords (will they never cease?)
  20. #  1/96 - user-selectable comment and continuation characters
  21. #         complete F90 keyword set (Thomas Bewley <bewley@rayleigh.stanford.edu>) 
  22. #         F90 functions and comparison operators optionally colorized ( " " )
  23. #         more complete set of C preprocessor commands colorized
  24. #         fixed case-sensitivity problem in line-indent routines
  25. #  1/96 - minor FortDblClick bug fix
  26. # 12/95 - more complete keyword set for F90 and HPF (from Tom Scavo)
  27. # 12/95 - cpp keyword colorization (George Nurser <g.nurser@soc.soton.ac.uk>)
  28. #         cmd-dbl-click supports cpp #include now
  29. # 11/95 - added FortBreakLine
  30. #         fixed case-sensitivity bug
  31. # 10/95 - fixed Cmd-Dbl-Click handler to deal w/ new(?) tag file format and
  32. #            improve performance (fortFindSub)
  33. #  9/95 - fixed getFortPrev bug with numbered lines
  34. #       - shiftLeft/Right revert to normal behavior on ill-formatted lines
  35. #  8/95 - auto-indentation is finally speedy and robust
  36. #  5/95 - added Cmd-Dbl-Click handler
  37. #       - added auto-indentation
  38. # 12/94 - fixed funcExpr, FortMarkFile search expressions
  39. #       - changed comment character from 'C' to 'c' (should be case-insensitive!)
  40. #       - added 'include' keyword
  41. #       - added FortShiftRight and FortShiftLeft procs
  42. #------------------------------------------------------------------------------
  43.  
  44.  
  45. #================================================================================
  46. if {$startingUp} {
  47.     addMode Fort dummyFort {*.f *.inc *.INC *.fcm *.for *.FOR *.f9 *.f90 *.hpf } {}
  48.     return
  49. }
  50.  
  51.  
  52.  
  53. proc dummyFort {} {}
  54.  
  55. newModeVar Fort sortedIsDefault    {0} 1
  56. newModeVar Fort wordWrap        {0}    1
  57. newModeVar Fort funcExpr    {^[^cC*!][ ¥t]*(subroutine|[ ¥ta-z*0-9]*function|entry).*$} 0
  58. newModeVar Fort autoMark        {0}    1
  59. newModeVar Fort electricTab        {1}    1
  60.  
  61. # newModeVar Fort    prefixString    {c}    0
  62. newModeVar Fort    continueChar    {$}    0
  63. newModeVar Fort    commentChar    {c}    0
  64. newModeVar Fort    colorFuncs    {0}    1
  65. newModeVar Fort    colorOpers    {0}    1
  66.  
  67. newModeVar Fort indentComment    {0}    1
  68. newModeVar Fort markTag            {{}} 0
  69.  
  70. #=============================================================================
  71. # Colorize Fortran keywords
  72. #
  73. proc fortColorKeywords {{color blue} {comment red} {specialChars black}} {
  74.     global FortmodeVars
  75.  
  76.     set FortKeywords { 
  77.         allocatable allocate assign backspace block call character close common 
  78.         complex contains continue cycle data deallocate dimension do double else 
  79.         elseif end enddo endfile endif entry equivalence exit external extrinsic 
  80.         forall format function goto if implicit include inquire integer intent 
  81.         interface intrinsic logical module namelist nullify open optional 
  82.         parameter pause pointer precision print private program public pure read 
  83.         real recursive return rewind save sequence stop subroutine target then 
  84.         use where while write assignment case default elsewhere endfile go none 
  85.         operator procedure select to type
  86.     }
  87.     
  88.     if {$specialChars != "black"} {
  89.         regModeKeywords -e $FortmodeVars(commentChar) -c $comment -k $color Fort $FortKeywords  -i {=}  -i {*}  -i {/}  -i {+}  -i {-}  -i {,}  -i {(} -i {)} -I $specialChars
  90.     } else {
  91.         regModeKeywords -e $FortmodeVars(commentChar) -c $comment -k $color Fort $FortKeywords  
  92.     }
  93.     unset FortKeywords
  94.  
  95. #=============================================================================
  96. # Colorize selected C preprocessor keywords
  97. #
  98. proc fortColorCPP {{color green}} {
  99.     set CPPKeywords  {
  100.         #if #endif #include #else #define #undef #ifdef #ifndef
  101.     }
  102.     regModeKeywords -a  -k $color Fort $CPPKeywords
  103.     unset CPPKeywords
  104. }
  105.  
  106.  
  107. #=========================================================================
  108. # Colorize Fortran operators
  109. #
  110. proc fortColorOpers {{color green}} {
  111.     set FortOperators {
  112.         eq ne lt le gt ge not and or eqv neqv true false
  113.     }
  114.     regModeKeywords -a -k $color Fort $FortOperators
  115.     unset FortOperators
  116. }
  117.  
  118. #=========================================================================
  119. # Colorize Fortran function keywords
  120. #
  121. proc fortColorFuncs {{color green}} {
  122.     # Fortran bit functions
  123.     #
  124.     set BitKeywords {
  125.         bit_size btest iand ibclr ibits ibset ieor ior ishft ishftc mvbits not
  126.     }
  127.     regModeKeywords -a -k $color Fort $BitKeywords
  128.     unset BitKeywords
  129.     
  130.     # Fortran intrinsic functions
  131.     #
  132.     set IntrinsicKeywords {
  133.         abs acos aimag asin atan atan2 conjg cos cosh dble dim dprod exp ichar 
  134.         len lge lgt lle llt log log10 max min mod sign sin sinh sqrt tan tanh 
  135.         iabs dabs cabs dacos dint dnint dasin datan datan2 dcos ccos dcosh idim 
  136.         ddim dexp cexp ifix idint alog ddlog clog alog10 dlog10 max0 amax0 max1 
  137.         amax1 dmax1 min0 amin0 min1 amin1 dmin1 amod dmod idnint float sngl 
  138.         isign dsign dsin csin dsinh dsqrt csqrt dtan dtanh aint anint char cmplx 
  139.         index int nint achar adjustl adjustr all allocated any associated 
  140.         bit_size btest ceiling count cshift date_and_time digits dot_product 
  141.         eoshift epsilon exponent floor fraction huge iachar iand ibclr ibits 
  142.         ibset ieor ior ishft ishftc kind lbound len_trim logical matmul 
  143.         maxexponent maxloc maxval merge minexponent minloc minval modulo mvbits 
  144.         nearest not pack precision present product radix random_number 
  145.         random_seed range repeat reshape rrspacing scale scan selected_int_kind 
  146.         selected_real_kind set_exponent shape size spacing spread sum 
  147.         system_clock tiny transfer transpose trim ubound unpack verify
  148.     }
  149.     regModeKeywords -a -k $color Fort $IntrinsicKeywords
  150.     unset IntrinsicKeywords    
  151. }
  152.  
  153. fortColorKeywords blue red magenta
  154. fortColorCPP green
  155.  
  156. #=============================================================================
  157. # Special Fortran keybindings
  158. #
  159. bind '¥[' <c>  FortShiftLeft Fort
  160. bind '¥[' <co> FortShiftLeftSpace Fort
  161. bind '¥]' <c>  FortShiftRight Fort
  162. bind '¥]' <co> FortShiftRightSpace Fort
  163.  
  164. bind '¥t'       doATab Fort
  165. bind '¥t' <o>     {doATab 1} Fort
  166. bind '¥t' <z>     {doATab 1} Fort
  167.  
  168. bind 'j'  <zo> FortBreakLine Fort
  169.  
  170. trace variable FortmodeVars(commentChar) w shadowFort
  171. trace variable FortmodeVars(colorFuncs) w shadowFort
  172. trace variable FortmodeVars(colorOpers) w shadowFort
  173.  
  174. #=============================================================================
  175. # Update colorization when Fortran mode variables are changed
  176. #
  177. proc shadowFort {name1 name2 op} {
  178.     global HOME FortmodeVars
  179.     if {$name1 == "FortmodeVars" && $op == "w"} {
  180.         switch $name2 {
  181.             "colorFuncs"    {
  182.                 if {$FortmodeVars(colorFuncs)} {
  183.                     fortColorFuncs green
  184.                 } else {
  185.                     fortColorFuncs black
  186.                 }
  187.              }
  188.             "colorOpers"    {
  189.                 if {$FortmodeVars(colorOpers)} {
  190.                     fortColorOpers green
  191.                 } else {
  192.                     fortColorOpers black
  193.                 }
  194.              }
  195.             "commentChar" {    
  196.                 fortColorKeywords blue red black
  197.             }
  198.             default {
  199.                 return
  200.             }
  201.         }
  202.     }
  203. }
  204.  
  205. #=============================================================================
  206. #
  207. proc FortMarkFile {} {
  208.     global FortmodeVars
  209.     set tag [quoteExpr2 $FortmodeVars(markTag)]
  210.     
  211.     set pat0 {^.*(subroutine|.*function|entry|program).*$}
  212.     set pat1 {^[^cC*!]([ ¥ta-z*0-9]*)(subroutine|.*function|entry|program)[ ¥t]+([a-z0-9_]+)}
  213.     set end [maxPos]
  214.     set pos 0
  215.     while {![catch {search -s -f 1 -r 1 -m 0 -i 1 $pat1 $pos} mtch]} {
  216.         regexp -nocase $pat1 [eval getText $mtch] allofit valtyp subtyp name
  217.         set start [lineStart [lindex $mtch 0]]
  218.         set next [nextLineStart $start]
  219.         set pos $next
  220.         if {! [regexp -nocase "end" $valtyp mtch]} {
  221.             set inds([lineStart $start]) $name
  222.         }
  223.         
  224.     }
  225.     
  226.     set pat2 "^(c+${tag})¥[ ¥t¥]*(¥[^¥n¥r¥]*¥[^ ¥t¥])¥[^ ¥t¥]*¥$"
  227.     set pos 0
  228.     while {![catch {search -s -f 1 -r 1 -m 0 -i 1 $pat2 $pos} mtch]} {
  229.         regexp -nocase $pat2 [eval getText $mtch] allofit cc comment
  230.         regsub -all {[¥/¥(¥)]} $comment {} comment
  231.         set start [lindex $mtch 0]
  232.         set end [nextLineStart $start]
  233.         set pos $end
  234.         set inds([lineStart $start]) $comment
  235.     }
  236.     
  237.     if {[info exists inds]} {
  238.         foreach f [lsort -integer [array names inds]] {
  239.             set next [nextLineStart $f ]
  240.             setNamedMark $inds($f) $f $f $f
  241.         }
  242.     }
  243. }
  244.  
  245. #================================================================================
  246. # Block shift left and right for Fortran mode (preserves cols 1-6)
  247. #================================================================================
  248.  
  249. proc FortShiftLeft {} {
  250.     global shiftChar
  251.     doFortShiftLeft "¥t"
  252.     
  253. }
  254. proc FortShiftLeftSpace {} {
  255.     global shiftChar
  256.     doFortShiftLeft " "
  257. }
  258.  
  259. proc doFortShiftLeft {shiftChar} {
  260.     set start [lineStart [getPos]]
  261.     set end [nextLineStart [expr [selEnd] - 1]]
  262.     if {$start >= $end} {set end [nextLineStart $start]}
  263.     
  264.     set text [split [getText $start [expr $end - 1]] "¥r"]
  265.     
  266.     set textout ""
  267.     
  268.     set pat {^([cC]|[ 0-9][ 0-9][ 0-9][ 0-9][ 0-9].| *[0-9]*¥t)(.*)$}
  269.     foreach line $text {
  270.         if {[regexp $pat $line mtch pref body]} {
  271.             if {[string index $body 0] == $shiftChar} {
  272.                 lappend textout $pref[string range $body 1 end]
  273.             } else {
  274.                 lappend textout $line
  275.             }
  276.  
  277.         } elseif {[string index $line 0] == $shiftChar} {
  278.             lappend textout [string range $line 1 end]
  279.  
  280.         } else {
  281.             lappend textout $line
  282.         }
  283.     }
  284.  
  285.     set text [join $textout "¥r"]    
  286.     replaceText $start [expr $end - 1] $text
  287.     select $start [expr 1 + $start + [string length $text]]
  288. }
  289.  
  290. proc FortShiftRight {} {
  291.     global shiftChar
  292.     doFortShiftRight "¥t"
  293.     
  294. }
  295. proc FortShiftRightSpace {} {
  296.     global shiftChar
  297.     doFortShiftRight " "
  298. }
  299.  
  300. proc doFortShiftRight {shiftChar} {
  301.     set start [lineStart [getPos]]
  302.     set end [nextLineStart [expr [selEnd] - 1]]
  303.     if {$start >= $end} {set end [nextLineStart $start]}
  304.     
  305.     set text [split [getText $start [expr $end - 1]] "¥r"]
  306.     
  307.     set textout ""
  308.     
  309.     set pat {^([cC]|[ 0-9][ 0-9][ 0-9][ 0-9][ 0-9].| *[0-9]*¥t)(.*)$}
  310.     foreach line $text {
  311.         if {[regexp $pat $line mtch pref body]} {
  312.             lappend textout $pref$shiftChar$body
  313.         } else {
  314.             lappend textout $shiftChar$line
  315.         }
  316.     }
  317.     
  318.     set text [join $textout "¥r"]    
  319.     replaceText $start [expr $end - 1] $text
  320.     select $start [expr 1 + $start + [string length $text]]
  321. }
  322.  
  323. proc FortBreakLine {} {
  324.     global FortmodeVars
  325.     set pos [getPos]
  326.     set line [getText [lineStart $pos] [expr [nextLineStart $pos]-1]]
  327.     if {[regexp {^[cC*!]} $line char]} {
  328.         insertText "¥n$char "
  329.     } else {
  330.         set char $FortmodeVars(continueChar)
  331.         insertText "¥n     $char"
  332.     }
  333.     FortindentLine
  334. }
  335.  
  336. #=============================================================================
  337. # Cmd-double-clicking opens include files, jumps to subroutine definitions,
  338. # and follows tags.
  339. #
  340. proc FortDblClick {from to} {
  341.     global tagFile
  342.     set pat1 {^[^cC*!][ ¥ta-z*0-9]*(subroutine|.*function|entry)[ ¥t]+}
  343.     set incPat {^[^cC*!][ ¥t]*include[ ¥t]*['"]([^'"]+)['"]}
  344.  
  345.     # First check whether an 'include' was clicked
  346.     set line [getText [lineStart $from] [expr [nextLineStart $to] - 1]]
  347.     if {[regexp -nocase $incPat $line allofit fname]} {
  348.         set path [absolutePath $fname]
  349.         if {[catch {openFileQuietly $path}]} { 
  350.             message "include file ¥'$fname¥' not found in source folder"
  351.         }
  352.         return
  353.     }
  354.     
  355.     select $from $to
  356.     set text [getSelect]
  357.     
  358.     # First check current file for subroutine definition,...
  359.     if {![catch {fortFindSub $text} mtch]} { 
  360.         regexp -nocase $pat1 [eval getText $mtch] allofit subtyp name
  361.         pushMark
  362.         display [lindex $mtch 0]
  363. #         eval select $mtch
  364.         message "press <Ctl .> to return to original cursor position"
  365.  
  366.     # ...then check tags file.
  367.     } else {
  368.         message "Searching tags file..."
  369.         set lines [grep "^$text'" $tagFile]
  370.         if {[regexp {'(.*)'} $lines dummy fname]} { 
  371.             pushMark
  372.             if {[string match "*$fname*" [winNames -f]]} {
  373.                 bringToFront $fname
  374.             } else {
  375.                 edit $fname
  376.             }
  377.             set inds [fortFindSub $text]
  378. #             set inds [search -s -f 1 -r 1 -i 1 "$pat1$text" 0]
  379.             display [lindex $inds 0]
  380. #             eval select $inds
  381.             message "press <Ctl .> to return to original cursor position"
  382.         }
  383.     }
  384. }
  385.  
  386. # Speedy search for a Fortran subroutine.  Performance is dramatically 
  387. # improved by scanning for the name alone first, rather than running 
  388. # complicated regexp search on the entire file.
  389. #
  390. proc fortFindSub {name} {
  391.     set pat1 {^[^cC*!][ ¥ta-z*0-9]*(subroutine|.*function|entry)[ ¥t]+}
  392.     set pos 0
  393.     while {![catch {search -s -f 1 -r 0 -m 0 -i 1 $name $pos} mtch]} {
  394.         set beg [lineStart [lindex $mtch 0]]
  395.         set end [expr [nextLineStart [lindex $mtch 1]] -1]
  396.         set line [getText $beg $end]
  397.         if {[regexp  -nocase $pat1$name $line allofit subtyp name]} {
  398.             return $mtch 
  399.         } else {
  400.             set pos [lindex $mtch 1]
  401.         }
  402.     }
  403.     error "Subroutine ¥"$name¥" not found"
  404. }
  405.  
  406. #=============================================================================
  407. # Fortan auto-indentation
  408. #
  409. # Logic:
  410. #    0.    Identify previous line
  411. #            a) ignore comments and continuation lines
  412. #            b) if current line is a CONTINUE that matches a DO, use the
  413. #                first corresponding DO as the previous line
  414. #
  415. #    1.    Find leading whitespace for previous line
  416. #
  417. #    2.    Increase whitespace if previous line starts a block, i.e.,
  418. #            a) DO loop
  419. #            b) IF ... THEN 
  420. #            c) ELSE
  421. #
  422. #    3.    Decrease whitespace if current line ends a block, i.e.,
  423. #            a) ELSE || ENDIF || END IF || ENDDO || END DO
  424. #            b) <linenum> CONTINUE matching a preceding DO
  425. #
  426. #        or if previous line ends a DO loop on an executable statement, i.e.,
  427. #            c) <linenum> (not CONTINUE) matching a preceding DO
  428. #
  429. ####################################################################################
  430. # Fortan auto-indentation
  431. #
  432. proc FortindentLine {} {    
  433.     set bol [lineStart [getPos]]
  434.     set eol [expr [nextLineStart $bol] - 1]
  435.     Fortindent $bol $eol
  436. }
  437.  
  438. proc FortindentRegion {} {    
  439.     Fortindent [getPos] [selEnd]
  440. }
  441.  
  442. ####################################################################################
  443. # Fortan auto-indentation of a specified region
  444. #
  445. proc Fortindent {pos0 pos1} {
  446.     global fortDooz fortPrevLine fortTop msg
  447.     global FortmodeVars
  448.  
  449.     set tag [quoteExpr2 $FortmodeVars(markTag)]
  450.     set doComment $FortmodeVars(indentComment)
  451.  
  452.     # Define regexps
  453.     set subPat {^[^cC*!][ ¥ta-z*0-9]*(subroutine|.*function|entry|program)[ ¥t]+([a-z0-9_]+)}
  454.     set bolPat {^[^cC*!¥n¥r][ ¥t]*[^ ¥t¥n¥r][^¥r¥n]*$}
  455.     set mtPat {^[ ¥t]*$}
  456.     set tab "    "
  457.     
  458.     set contPat {^     ([^ ¥t¥n¥r])[^¥r¥n]*$}
  459.     set lnumPat {^([ ¥t]*)([0-9]*)([ ¥t]*)(.*)$}
  460.     set comPat "^(¥[cC*!¥]+(${tag})?)(¥[ ¥t¥]*)(.*)¥$"
  461.     set doPat {^[^cC*!¥n¥r][ ¥t]*do[ ¥t]+}
  462.     set tailPat {[^¥r¥n]*$}
  463.     
  464.     set bobPat {^(if[^¥n¥r]*then|else|do)}
  465.     set eobPat {^(end[ ¥t]*if|end[ ¥t]*do|else)}
  466.     set enddoPat {^(end[ ¥t]*do|continue)}
  467.     
  468. #     set fortTop [fortSubTop $pos0]
  469.     set fortTop -1
  470.     
  471.     catch {unset fortDooz}
  472.     set fortPrevLine ""
  473.     
  474.     # Loop over region line by line
  475.     set from [lindex [posToRowCol $pos0] 0]
  476.     set to [lindex [posToRowCol $pos1] 0]
  477.     
  478.     while {$from <= $to} {        
  479.         set msg "Indenting line $from"
  480.         message $msg
  481.         set bol [lineStart [rowColToPos $from 0]]
  482.         set eol [expr [nextLineStart $bol] - 1]
  483.         set thisLine [getText $bol $eol]
  484.         goto $bol
  485.         
  486.         # Check whether we're entering a new routine
  487.         #
  488.         if {[regexp $subPat $thisLine allofit subType subName]} {
  489.             # alertnote "entering subr: ¥/$subName¥/"
  490.             set fortTop $bol
  491.             catch {unset fortDooz}
  492.         } 
  493.         
  494.         # Is the current line a comment line...
  495.         #        
  496.         if {[regexp $comPat $thisLine allofit cc tag pre body]} {
  497.             if {$FortmodeVars(indentComment) > 0} {
  498.                 set body [string trimright $body]
  499.                 # alertnote "comment line: ¥/$pre¥/$body¥/"
  500.                 set lwhite "$cc     "
  501.                 
  502.                 replaceText $bol $eol $lwhite$body
  503.             }
  504.             
  505.         # ... or a line of code (possibly empty)?
  506.         #    
  507.         } elseif {[regexp $lnumPat $thisLine allofit pre lnum post body]} {
  508.             set body [string trimright $body]
  509.             # alertnote "line: ¥/$pre¥/$lnum¥/$post¥/$body¥/"
  510.             
  511.             # is it a continuation line?
  512.             #
  513.             if {(![regexp {¥t} $pre]) && ([string length $pre] == 5)} {
  514.                 set cont [string index $lnum$post$body 0]
  515.                 set body [string trimleft [string range $lnum$post$body 1 end]]
  516.             } else {
  517.                 set cont {}
  518.             }
  519.             # alertnote "cont: ¥/$cont¥/"
  520.             
  521.             # get whitespace for preceding line
  522.             set enddo [getFortPrev $bol $lnum]
  523.             set lwhite [getFortLwhite $bol]
  524.             
  525.             # if this line ends a block, decrease the whitespace
  526.             if {[regexp $eobPat $body] || ($enddo && [regexp -nocase $enddoPat $body])} {
  527.                 set lwlen [expr [string length $lwhite] - 4]
  528.                 set lwhite [string range $lwhite 0 $lwlen]
  529.             } 
  530.             
  531.             if {[string length $lnum]} {
  532.                 if {[string index $lwhite 0] != $tab} {
  533.                     set lwhite [string range $lwhite [expr [string length $lnum] +1] end]
  534.                 }
  535.                 set lnum " $lnum"
  536.             }
  537.             # alertnote "lwhite: ¥/$lwhite¥/ len: [string length $lwhite]"
  538.             # message "$msg : replacing text      "
  539.             
  540.             if {[string length $cont]} {
  541.                 replaceText $bol $eol "     $cont$lwhite$body"    
  542.             } else {
  543.                 replaceText $bol $eol $lnum$lwhite$body
  544.                 if {[string length $body] > 0} {
  545.                     set fortPrevLine $lnum$lwhite$body
  546.                 }
  547.             }
  548.         } else {
  549.             # message "$msg : Couldn't parse line         "
  550.         }
  551.         
  552.         # message "$msg : Done                "
  553.         incr from
  554.     }
  555. }
  556.  
  557. proc getFortLwhite {bol} {
  558.     global fortDooz fortPrevLine fortTop msg
  559.     # Define regexps
  560.     set tab "    "
  561.     set lnumPat {^([ ¥t]*)([0-9]*)([ ¥t]*)(.*)$}
  562.     set doPat {^[^cC*!¥n¥r][ ¥t]*do[ ¥t]+}
  563.     set bobPat {^(if[^¥n¥r]*then|else|do)}
  564.     set enddoPat {^(end[ ¥t]*do|continue)}
  565.     
  566.     if {[regexp $lnumPat $fortPrevLine allofit pre0 lnum0 post0 body0]} {
  567.         # alertnote "prevLine: ¥/$pre0¥/$lnum0¥/$post0¥/$body0¥/"
  568.         
  569.         if {[string length $lnum0]} {
  570.             if {[string index $post0 0] == $tab} {
  571.                 set lwhite $post0
  572.             } else {
  573.                 regsub -all {[0-9]} $pre0$lnum0$post0 { } lwhite
  574.             }
  575.         } else {
  576.             set lwhite $pre0
  577.         }
  578.         # alertnote "lwhite: ¥/$lwhite¥/ len: [string length $lwhite]"
  579.         # message "$msg : got lwhite (initial)"
  580.         
  581.         # if there's a line number and it's not a CONTINUE or ENDDO, 
  582.         # then check for a matching DO statement and adjust 
  583.         # indentation if found
  584.         #
  585.         if {[string length $lnum0] && ![regexp -nocase $enddoPat $body0]} {
  586.             if {[getFortPrev [lineStart [expr $bol - 1]] $lnum0]} {
  587.                 set lwlen [expr [string length $lwhite] - 4]
  588.                 set lwhite [string range $lwhite 0 $lwlen]
  589.  
  590.             }
  591.         }
  592.         
  593.         # If the preceeding line begins a block (IF-THEN, DO, or ELSE),
  594.         # then increase the whitespace
  595.         #    
  596.         if {[regexp -nocase $bobPat $body0]} {
  597.             set lwhite "$lwhite   "
  598.             
  599.             if {[regexp -nocase "$doPat¥(¥[0-9¥]+¥)" $body0 mtch donum]} {
  600.                 set eol [expr [nextLineStart $bol] - 1]
  601.                 set fortDooz($donum) [getText $bol $eol]
  602.             }
  603.         }
  604.         # message "$msg : got lwhite (final)  "
  605.     }
  606.     return "$lwhite"
  607. }
  608.  
  609. proc getFortPrev {bol lnum} {        
  610.     global fortDooz fortPrevLine fortTop msg
  611.     # Define regexps
  612.     set doPat {^[^cC*!¥n¥r][ ¥t]*do[ ¥t]+}
  613.     set bolPat {^[^cC*!¥n¥r][ ¥t]*[^ ¥t¥n¥r][^¥r¥n]*$}
  614.     set contPat {^     ([^ ¥t¥n¥r])[^¥r¥n]*$}
  615.  
  616.     # if there's a line number, check for a matching DO statement ...
  617.     if {[string length $lnum]} {
  618.         if {[lsearch [array names fortDooz] $lnum] >= 0} {
  619.             set fortPrevLine $fortDooz($lnum)
  620.             return 1
  621.         } else {
  622.             if {$fortTop < 0} {
  623.                 set fortTop [fortSubTop $bol]
  624.             }
  625.             if {![catch {search -s -f 0 -r 1 -i 1 -l $fortTop $doPat$lnum [expr $bol -1]} dolst]} {
  626.                 set fortPrevLine [eval getText $dolst]
  627.                 set fortDooz($lnum) $fortPrevLine
  628.                 # alertnote "doLine0: ¥/$fortPrevLine¥/"
  629.                 return 1
  630.             }
  631.         }
  632.     }
  633.         
  634.     # ... otherwise find the first preceding non-comment, non-continuation line
  635.     if {[string length $fortPrevLine] == 0} {
  636.         if {[catch {
  637.             set lst [search -s -f 0 -r 1 -i 1 -s $bolPat [expr $bol-1]]
  638.             set fortPrevLine [eval getText $lst]
  639.             while {[regexp -nocase $contPat $fortPrevLine]} {
  640.                 set lst [search -s -f 0 -r 1 -i 1 $bolPat [expr [lindex $lst 0] - 1]]
  641.                 set fortPrevLine [eval getText $lst]
  642.             }
  643.         }]} {
  644.             # if search fails, we're at the top of a file, so reset indentation
  645.             set fortPrevLine "      continue"
  646.         }
  647.     }
  648.     
  649.     # alertnote "prevLine: ¥/$fortPrevLine¥/"
  650.     # message "$msg : got prevLine"
  651.     return 0
  652. }
  653.  
  654. # Find the beginning of the current subroutine
  655. #
  656. proc fortSubTop {{pos 0}} {
  657.     if {$pos == 0} {
  658.         set pos [lineStart [getPos]]
  659.     }
  660.     set subPat {^[^cC*!][ ¥ta-z*0-9]*(subroutine|.*function|entry|program)[ ¥t]+([a-z0-9_]+)}
  661.     
  662.     if {![catch {search -s -f 0 -r 1 -m 0 -i 1 $subPat $pos} sublst]} {
  663.         # set subLine [eval getText $sublst]
  664.         # alertnote "subLine: ¥/$subLine¥/"
  665.         return [lindex $sublst 0]
  666.     } else {
  667.         return 0
  668.     } 
  669. }